package com.xm.rsi.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xm.rsi.common.ErrorCode;
import com.xm.rsi.exception.BusinessException;
import com.xm.rsi.mapper.UserMapper;
import com.xm.rsi.model.entity.User;
import com.xm.rsi.model.menus.UserRoleEnum;
import com.xm.rsi.model.vo.LoginUserVO;
import com.xm.rsi.model.vo.UserVO;
import com.xm.rsi.service.UserService;
import com.xm.rsi.utils.TokenGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;

import static com.xm.rsi.constant.UserConstant.USER_LOGIN_STATE;

/**
 * @author xmm
 * @description 针对表【user】的数据库操作Service实现
 * @createDate 2023-09-29 18:59:39
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    private static final String SALT = "XM";

    public static final String AUTH_TOKEN = "XMRSI";

    public static final int TIME = 30 * 24 * 60 * 60;

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public long userRegister(String userEmail, String userPassword, String checkPassword, String userName, String emailCode) {
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        String code = redisTemplate.opsForValue().get(userEmail);
        if (!emailCode.equals(code)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱验证码错误！");
        }
        synchronized (userEmail.intern()) {
            // 账户不能重复
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userEmail", userEmail);
            long count = userMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
            }
            // 2. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            // 3. 分配 accessKey, secretKey
            String accessKey = DigestUtil.md5Hex(SALT + userEmail + RandomUtil.randomNumbers(5));
            String secretKey = DigestUtil.md5Hex(SALT + userEmail + RandomUtil.randomNumbers(8));
            // 4. 插入数据
            User user = new User();
            user.setUserEmail(userEmail);
            user.setUserPassword(encryptPassword);
            user.setAccessKey(accessKey);
            user.setSecretKey(secretKey);
            user.setUserName(userName);
            if (!this.save(user)) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
            redisTemplate.delete(userEmail);
            return user.getId();
        }
    }

    @Override
    public Long addOne(String userEmail, String userPassword, String checkPassword, String userName) {
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userEmail", userEmail);
        User u = userMapper.selectOne(queryWrapper);
        if (u != null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
        }
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        String accessKey = DigestUtil.md5Hex(SALT + userEmail + RandomUtil.randomNumbers(5));
        String secretKey = DigestUtil.md5Hex(SALT + userEmail + RandomUtil.randomNumbers(8));
        User user = new User();
        user.setUserEmail(userEmail);
        user.setUserPassword(encryptPassword);
        user.setAccessKey(accessKey);
        user.setSecretKey(secretKey);
        user.setUserName(userName);
        if (!this.save(user)) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        return user.getId();
    }

    @Override
    public Boolean userUpdate(String userEmail, String userPassword, String checkPassword, String userName, Integer userState, String userRole) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userEmail", userEmail);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号不存在！");
        }
        if (userPassword != null && !userPassword.isEmpty()) {
            if (userPassword.length() < 8 || checkPassword.length() < 8) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
            }
            if (!userPassword.equals(checkPassword)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
            }
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            user.setUserPassword(encryptPassword);
        }
        if (userName == null || userName.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名不能为空");
        }
        if (userState == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户状态不能为空");
        }
        user.setUserName(userName);
        user.setUserState(userState);
        user.setUserRole(userRole);
        return this.updateById(user);
    }

    @Override
    public LoginUserVO userLogin(String userEmail, String userPassword, boolean isRemember, HttpServletRequest request, HttpServletResponse response) {
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userEmail", userEmail);
        queryWrapper.eq("userPassword", encryptPassword);
        User user = this.baseMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号或密码不正确");
        }
        if (user.getUserState() == 1) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "账号已被封禁，请联系管理员");
        }
        request.getSession().setAttribute(USER_LOGIN_STATE, getUserVO(user));
        if (isRemember) {
            String token = TokenGenerator.generateToken(user.getUserEmail(), user.getUserPassword(), TIME + System.currentTimeMillis() / 1000, AUTH_TOKEN);
            user.setToken(token);
            this.updateById(user);
            Cookie cookie = new Cookie("rsiToken", token);
            cookie.setMaxAge(TIME);
            cookie.setPath(request.getContextPath() + "/");
            response.addCookie(cookie);
        } else {
            user.setToken("");
            this.updateById(user);
            Cookie cookie = new Cookie("rsiToken", null);
            cookie.setMaxAge(-1);
            cookie.setPath(request.getContextPath() + "/");
            response.addCookie(cookie);
        }
        return this.getLoginUserVO(user);
    }

    @Override
    public boolean userLogout(HttpServletRequest request, HttpServletResponse response) {
        if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "账号未登录");
        }
        // 移除登录态
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        Cookie cookie = new Cookie("rsiToken", null);
        cookie.setMaxAge(-1);
        cookie.setPath(request.getContextPath() + "/");
        response.addCookie(cookie);
        return true;
    }

    @Override
    public String changePwd(String userEmail, String userPassword, String checkPassword, String emailCode) {
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        String code = redisTemplate.opsForValue().get(userEmail);
        if (!emailCode.equals(code)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱验证码错误！");
        }
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userEmail", userEmail);
        User user = this.getOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该用户不存在！");
        }
        user.setUserPassword(encryptPassword);
        if (this.updateById(user)) {
            redisTemplate.delete(userEmail);
            return "修改成功";
        } else {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    @Override
    public User getLoginUser(HttpServletRequest request) {
        UserVO currentUser = (UserVO) request.getSession().getAttribute(USER_LOGIN_STATE);
        if (currentUser != null) {
            Long id = currentUser.getId();
            User user = this.getById(id);
            if (user == null) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "该用户不存在");
            }
            return user;
        }
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "请重新登录");
        }
        String rsiToken = null;
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            if (name.equals("rsiToken")) {
                rsiToken = cookie.getValue();
            }
        }
        if (rsiToken == null) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "请重新登录");
        }
        byte[] decode = Base64.getDecoder().decode(rsiToken);
        String chain = new String(decode);
        if (chain.split(":").length != 3) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "自动登录校验失败，请重新登录");
        }
        String userEmail = chain.split(":")[0];
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userEmail", userEmail);
        User user = getOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }
        if (!user.getToken().equals(rsiToken)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "自动登录校验失败，请重新登录");
        }
        long time = Long.parseLong(chain.split(":")[1]);
        if (time <= System.currentTimeMillis() / 1000) {
            user.setToken("");
            updateById(user);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "登录过期，请重新登录");
        }
        String md5Str = chain.split(":")[2];
        String md5Hex = DigestUtil.md5Hex(userEmail + ":" + user.getUserPassword() + ":" + time + ":" + AUTH_TOKEN);
        if (!md5Str.equals(md5Hex)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "自动登录校验失败，请重新登录");
        }
        request.getSession().setAttribute(USER_LOGIN_STATE, getUserVO(user));
        return user;
    }

    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtils.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    public boolean isAdmin(User user) {
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }

    @Override
    public boolean isRootAdmin(User user) {
        return user != null && UserRoleEnum.ROOT.getValue().equals(user.getUserRole());
    }

    @Override
    public Page<UserVO> getUserVOPage(long current, long pageSize, QueryWrapper<User> queryWrapper) {
        Page<User> userPage = this.page(new Page<>(current, pageSize), queryWrapper);
        List<User> userList = userPage.getRecords();
        Page<UserVO> userVOPage = new Page<>(current, pageSize, userPage.getTotal());
        List<UserVO> userVOList = userList.stream().map(this::getUserVO).collect(Collectors.toList());
        userVOPage.setRecords(userVOList);
        return userVOPage;
    }

}




